import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;

import ch.bfh.ti.barcode.colorQrCode.ColorQrCodeGenerator;
import ch.bfh.ti.barcode.exception.EncodeException;
import ch.bfh.ti.barcode.io.CodeOutputStream;
import ch.bfh.ti.barcode.multiple.CodeEvent;
import ch.bfh.ti.barcode.multiple.CodeEventListener;
import ch.bfh.ti.barcode.multiple.MultipleCodeGenerator;

public class Main implements CodeEventListener {
	private LinkedList<BufferedImage> images = new LinkedList<BufferedImage>();
	private final int time = 1000;
	private final String fileName = "C:\\Users\\dvburg\\Desktop\\fail.jpg";
	private final int codeSize = 462;

	public static void main(String args[]) {
		new Main();
	}

	public Main() {
		System.out.println("start");

		File file = new File(fileName);
		byte[] data = new byte[(int) file.length()];
		DataInputStream dis;
		try {
			dis = new DataInputStream(new FileInputStream(file));
			try {
				dis.readFully(data);
			} catch (IOException e1) {
				e1.printStackTrace();
			} finally {
				try {
					dis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} catch (FileNotFoundException e2) {
			e2.printStackTrace();
		}

		ColorQrCodeGenerator generator = new ColorQrCodeGenerator(codeSize);
		try {
			MultipleCodeGenerator multipleGenerator = new MultipleCodeGenerator(
					generator, 0, 0, data.length, file.getName(), 4);
			multipleGenerator.addEventListener(this);
			CodeOutputStream stream = new CodeOutputStream(multipleGenerator);
			try {
				stream.write(data);
				stream.flush();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					stream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		} catch (EncodeException e) {
			e.printStackTrace();
		}

		System.out.println(images.size());
		try {
			ImageOutputStream output = new FileImageOutputStream(new File(
					"out.gif"));

			// Get writer
			ImageWriter writer = null;
			Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
			if (!iter.hasNext()) {
				output.close();
				throw new RuntimeException("Image writer doesn't exist!");
			} else {
				writer = iter.next();
			}

			ImageWriteParam imageWriteParam = writer.getDefaultWriteParam();
			ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier
					.createFromBufferedImageType(images.get(0).getType());

			IIOMetadata imageMetaData = writer.getDefaultImageMetadata(
					imageTypeSpecifier, imageWriteParam);

			String metaFormatName = imageMetaData.getNativeMetadataFormatName();

			IIOMetadataNode root = (IIOMetadataNode) imageMetaData
					.getAsTree(metaFormatName);

			IIOMetadataNode graphicsControlExtensionNode = getNode(root,
					"GraphicControlExtension");

			graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
			graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
			graphicsControlExtensionNode.setAttribute("transparentColorFlag",
					"FALSE");
			graphicsControlExtensionNode.setAttribute("delayTime",
					Integer.toString(time / 10));
			graphicsControlExtensionNode.setAttribute("transparentColorIndex",
					"0");

			IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
			commentsNode.setAttribute("CommentExtension", "ti.bfh.ch");

			IIOMetadataNode appEntensionsNode = getNode(root,
					"ApplicationExtensions");

			IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");

			child.setAttribute("applicationID", "NETSCAPE");
			child.setAttribute("authenticationCode", "2.0");

			int loop = 0;

			child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF),
					(byte) ((loop >> 8) & 0xFF) });
			appEntensionsNode.appendChild(child);

			imageMetaData.setFromTree(metaFormatName, root);

			writer.setOutput(output);

			writer.prepareWriteSequence(null);

			for (BufferedImage image : images)
				writer.writeToSequence(new IIOImage(image, null, imageMetaData),
						imageWriteParam);
			
			output.flush();
			output.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static IIOMetadataNode getNode(IIOMetadataNode rootNode,
			String nodeName) {
		int nNodes = rootNode.getLength();
		for (int i = 0; i < nNodes; i++) {
			if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
				return ((IIOMetadataNode) rootNode.item(i));
			}
		}
		IIOMetadataNode node = new IIOMetadataNode(nodeName);
		rootNode.appendChild(node);
		return (node);
	}

	@Override
	public void codeSent(CodeEvent e) {
		int[] data = e.getData();
		int size = (int) Math.sqrt(data.length);
		BufferedImage image = new BufferedImage(size, size,
				BufferedImage.TYPE_INT_ARGB);
		image.setRGB(0, 0, size, size, data, 0, size);
		
		BufferedImage scaled = new BufferedImage(size*4, size*4, BufferedImage.TYPE_INT_ARGB);
		AffineTransform at = new AffineTransform();
		at.scale(4.0, 4.0);
		AffineTransformOp scaleOp = 
		   new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		scaled = scaleOp.filter(image, scaled);
		images.add(scaled);
	}
}
